/*
 * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "hifb_blit.h"
#include "hi_tde_ext.h"
#include "mod_ext.h"

#include <linux/hardirq.h>

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

#ifdef __HuaweiLite__
#define in_atomic() (HI_FALSE)
#endif
extern bool g_bUpdateRotateRect;

static IntCallBack g_tde_call_back;
static IntCallBack g_tde_rotate_call_back;

/* tde support which color format */
static HI_BOOL g_bTdeColFmt[HIFB_FMT_BUTT] = {
    HI_FALSE, /* HIFB_FMT_RGB565 */
    HI_FALSE, /* HIFB_FMT_RGB888 */
    HI_FALSE, /* HIFB_FMT_KRGB444 */
    HI_FALSE, /* HIFB_FMT_KRGB555 */
    HI_FALSE, /* HIFB_FMT_KRGB888 */
    HI_TRUE,  /* HIFB_FMT_ARGB4444 */
    HI_TRUE,  /* HIFB_FMT_ARGB1555 */
    HI_TRUE,  /* HIFB_FMT_ARGB8888 */
    HI_FALSE, /* HIFB_FMT_ARGB8565 */
    HI_FALSE, /* HIFB_FMT_RGBA4444 */
    HI_FALSE, /* HIFB_FMT_RGBA5551 */
    HI_FALSE, /* HIFB_FMT_RGBA5658 */
    HI_FALSE, /* HIFB_FMT_RGBA8888 */

    HI_FALSE, /* BGR565 */
    HI_FALSE, /* BGR888 */
    HI_FALSE, /* ABGR4444 */
    HI_FALSE, /* ABGR1555 */
    HI_FALSE, /* ABGR8888 */
    HI_FALSE, /* ABGR8565 */
    HI_FALSE, /* BGR444 16bpp */
    HI_FALSE, /* BGR555 16bpp */
    HI_FALSE, /* BGR888 32bpp */

    HI_FALSE, /* HIFB_FMT_1BPP */
    HI_FALSE, /* HIFB_FMT_2BPP */
    HI_FALSE, /* HIFB_FMT_4BPP */
    HI_FALSE, /* HIFB_FMT_8BPP */
    HI_FALSE, /* HIFB_FMT_ACLUT44 */
    HI_FALSE, /* HIFB_FMT_ACLUT88 */
    HI_FALSE, /* HIFB_FMT_PUYVY */
    HI_FALSE, /* HIFB_FMT_PYUYV */
    HI_FALSE, /* HIFB_FMT_PYVYU */
    HI_FALSE, /* HIFB_FMT_YUV888 */
    HI_FALSE, /* HIFB_FMT_AYUV8888 */
    HI_FALSE, /* HIFB_FMT_YUVA8888 */
};

static hi_tde_color_fmt g_hifb_fmt[HIFB_FMT_BUTT] =  {
    HI_TDE_COLOR_FMT_RGB565, HI_TDE_COLOR_FMT_RGB888,
    HI_TDE_COLOR_FMT_RGB444, HI_TDE_COLOR_FMT_RGB555,
    HI_TDE_COLOR_FMT_ARGB8888, HI_TDE_COLOR_FMT_ARGB4444,
    HI_TDE_COLOR_FMT_ARGB1555, HI_TDE_COLOR_FMT_ARGB8888,
    HI_TDE_COLOR_FMT_ARGB8565, HI_TDE_COLOR_FMT_MAX,
    HI_TDE_COLOR_FMT_MAX, HI_TDE_COLOR_FMT_MAX,
    HI_TDE_COLOR_FMT_MAX, HI_TDE_COLOR_FMT_BGR565,
    HI_TDE_COLOR_FMT_BGR888, HI_TDE_COLOR_FMT_ABGR4444,
    HI_TDE_COLOR_FMT_ABGR1555, HI_TDE_COLOR_FMT_ABGR8888,
    HI_TDE_COLOR_FMT_ABGR8565, HI_TDE_COLOR_FMT_BGR444,
    HI_TDE_COLOR_FMT_BGR555, HI_TDE_COLOR_FMT_ABGR8888,
    HI_TDE_COLOR_FMT_CLUT1, HI_TDE_COLOR_FMT_CLUT2,
    HI_TDE_COLOR_FMT_CLUT4, HI_TDE_COLOR_FMT_CLUT8,
    HI_TDE_COLOR_FMT_ACLUT44, HI_TDE_COLOR_FMT_ACLUT88,
    HI_TDE_COLOR_FMT_MAX, HI_TDE_COLOR_FMT_MAX,
    HI_TDE_COLOR_FMT_MAX, HI_TDE_COLOR_FMT_MAX,
    HI_TDE_COLOR_FMT_MAX, HI_TDE_COLOR_FMT_MAX,
};

HI_BOOL hifb_TdeIsSupportFmt(HIFB_COLOR_FMT_E fmt)
{
    if (fmt >= HIFB_FMT_BUTT) {
        return HI_FALSE;
    }
    return g_bTdeColFmt[fmt];
}
hi_tde_color_fmt HIFB_DRV_ConvFmt(HIFB_COLOR_FMT_E Fmt)
{
    if (Fmt >= HIFB_FMT_BUTT) {
        return HI_TDE_COLOR_FMT_MAX;
    }
    return g_hifb_fmt[Fmt];
}

HI_S32 HIFB_DRV_Fill(HIFB_BUFFER_S *pDstImg, HI_U32 u32FillData)
{
    HI_S32 s32Ret;
    hi_tde_surface stDstSur = {0};
    hi_tde_rect stDstRect = {0};
    hi_s32 handle;
    hi_tde_export_func *pTdeExportFunc = NULL;

    pTdeExportFunc = FUNC_ENTRY(hi_tde_export_func, HI_ID_TDE);
    if ((pTdeExportFunc == NULL) || (pTdeExportFunc->drv_tde_module_begin_job == NULL) ||
        (pTdeExportFunc->drv_tde_module_end_job == NULL) || (pTdeExportFunc->drv_tde_module_cancel_job == NULL) ||
        (pTdeExportFunc->drv_tde_module_quick_fill == NULL)) {
        HI_TRACE_FB(HI_DBG_ERR, "TDE pTdeExportFunc is NULL!\n");
        return HI_FAILURE;
    }

    /* config dst */
    stDstSur.phy_addr = pDstImg->stCanvas.u64PhyAddr;
    stDstSur.width = pDstImg->stCanvas.u32Width;
    stDstSur.height = pDstImg->stCanvas.u32Height;
    stDstSur.stride = pDstImg->stCanvas.u32Pitch;
    stDstSur.alpha_max_is_255 = HI_TRUE;
    stDstSur.is_ycbcr_clut = HI_FALSE;
    stDstSur.color_fmt = HIFB_DRV_ConvFmt(pDstImg->stCanvas.enFmt);

    stDstRect.pos_x = 0;
    stDstRect.pos_y = 0;
    stDstRect.height = stDstSur.height;
    stDstRect.width = stDstSur.width;

    s32Ret = pTdeExportFunc->drv_tde_module_begin_job(&handle);
    if (s32Ret != HI_SUCCESS) {
        osal_printk("HiFB Warning: TDE begin job failed\n");
        return s32Ret;
    }

    s32Ret = pTdeExportFunc->drv_tde_module_quick_fill(handle, &stDstSur, &stDstRect, u32FillData);
    if (s32Ret != HI_SUCCESS) {
        osal_printk("HiFB Warning: TDE blit failed. ret = 0x%x handle %d \n", s32Ret, handle);
        pTdeExportFunc->drv_tde_module_cancel_job(handle);
        return s32Ret;
    }
    /* 1000 is timeout */
    s32Ret = pTdeExportFunc->drv_tde_module_end_job(handle, HI_TRUE, 1000, HI_FALSE, HI_NULL, HI_NULL);
    if (s32Ret != HI_SUCCESS) {
        osal_printk("HiFB Warning: FILL job submitted to TDE failed!!! Ret = 0x%x\n", s32Ret);
        return s32Ret;
    }

    return HI_SUCCESS;
}

static HI_U32 HIFB_DRV_GetDcmpStride(HIFB_SURFACE_S *pstCanvas)
{
    HI_U32 comp_ratio_int = 1000;
    HI_U32 u32BytePerPix = 2;
    HI_U32 extend_width;
    HI_U32 exp_num_temp = 0;
    HI_U32 u32Stride = pstCanvas->u32Pitch;

    if (pstCanvas->enFmt == HIFB_FMT_ARGB8888) {
        if (pstCanvas->u32Width <= 320) {         /* 320 alg data */
            exp_num_temp = 2;                     /* 2 alg data */
            comp_ratio_int = 1000;                /* 1000 alg data */
        } else if (pstCanvas->u32Width <= 720) {  /* 720 alg data */
            exp_num_temp = 10;                    /* 10 alg data */
            comp_ratio_int = 2000;                /* 2000 alg data */
        } else if (pstCanvas->u32Width <= 3840) { /* 3840 alg data */
            exp_num_temp = 10;                    /* 10 alg data */
            comp_ratio_int = 2000;                /* 2000 alg data */
        }
        /* 4 is PerPix byte */
        u32BytePerPix = 4;
        /* 8 1000 127 128 16 alg data */
        u32Stride = ((pstCanvas->u32Width * u32BytePerPix * 8 * 1000 / comp_ratio_int + 127) / 128 + exp_num_temp) * 16;
    } else if ((HIFB_FMT_ARGB1555 == pstCanvas->enFmt) || (HIFB_FMT_ARGB4444 == pstCanvas->enFmt)) {
        if (pstCanvas->u32Width <= 720) {           /* 720 alg data */
            exp_num_temp = 2;                       /* 2 alg data */
            comp_ratio_int = 1000;                  /* 1000 alg data */
        } else if (pstCanvas->u32Width <= 3840) {   /* 3840 alg data */
            exp_num_temp = 0;
            comp_ratio_int = 1000;                  /* 1000 alg data */
        }
        u32BytePerPix = 2; /* 2 is PerPix byte */

        extend_width = ((pstCanvas->u32Width + 31) / 32) * 32; /* 31 32 alg data */
        /* 8 1000 127 128 16 alg data */
        u32Stride = ((extend_width * u32BytePerPix * 8 * 1000 / comp_ratio_int + 127) / 128 + exp_num_temp) * 16;
    }

    u32Stride = (u32Stride + 0xf) & 0xfffffff0;

    return u32Stride;
}

static hi_void drv_blit_init(hi_tde_surface *surface, hi_tde_rect *rect,
                                 HIFB_BUFFER_S *img, HIFB_BLIT_OPT_S *opt)
{
    surface->phy_addr   = img->stCanvas.u64PhyAddr;
    surface->width  = img->stCanvas.u32Width;
    surface->height = img->stCanvas.u32Height;
    surface->stride = img->stCanvas.u32Pitch;
    surface->alpha_max_is_255 = HI_TRUE;
    surface->is_ycbcr_clut = HI_FALSE;
    surface->color_fmt = HIFB_DRV_ConvFmt(img->stCanvas.enFmt);
    surface->alpha0 = opt->stAlpha.u8Alpha0;
    surface->alpha1 = opt->stAlpha.u8Alpha1;

    rect->pos_x = img->UpdateRect.x;
    rect->pos_y = img->UpdateRect.y;
    rect->width = img->UpdateRect.w;
    rect->height = img->UpdateRect.h;
    return;
}

static hi_void drv_blit_compress(hi_tde_surface *dst_surface, HIFB_BUFFER_S *dst_img, HIFB_BLIT_OPT_S *opt)
{
    if (opt->bCompress == HI_TRUE) {
        dst_surface->cbcr_phy_addr = dst_img->stCanvas.u64GBPhyAddr;
        dst_surface->cbcr_stride = dst_img->stCanvas.u32Pitch;
        dst_surface->stride = HIFB_DRV_GetDcmpStride(&dst_img->stCanvas);
    }
    return;
}

static hi_void drv_blit_refresh_screen(hi_tde_surface *src_surface, hi_tde_rect *src_rect,
                                      hi_tde_surface *dst_surface, hi_tde_rect *dst_rect,
                                      hi_bool is_refresh_screen)
{
    if (is_refresh_screen == HI_TRUE) {
        dst_rect->pos_x   =  src_rect->pos_x * dst_surface->width / src_surface->width;
        dst_rect->pos_y   =  src_rect->pos_y * dst_surface->height / src_surface->height;
        dst_rect->width  =  src_rect->width * dst_surface->width / src_surface->width;
        dst_rect->height =  src_rect->height * dst_surface->height / src_surface->height;

        if (g_bUpdateRotateRect) {
            dst_rect->pos_x = dst_surface->width - dst_rect->width - dst_rect->pos_x;
            dst_rect->pos_y = dst_surface->height - dst_rect->height - dst_rect->pos_y;
        }
    }
    return;
}

static hi_void blit_opt_get_filter_mode(hi_tde_opt *option, HIFB_BLIT_OPT_S *opt)
{
    switch(opt->enAntiflickerLevel) {
        case HIFB_LAYER_ANTIFLICKER_NONE:
            option->filter_mode = HI_TDE_FILTER_MODE_COLOR;
            option->deflicker_mode= HI_TDE_DEFLICKER_LEVEL_MODE_NONE;
            break;
        case HIFB_LAYER_ANTIFLICKER_LOW:
            option->filter_mode = HI_TDE_FILTER_MODE_COLOR;
            break;
        case HIFB_LAYER_ANTIFLICKER_MIDDLE:
            option->filter_mode = HI_TDE_FILTER_MODE_COLOR;
            break;
        case HIFB_LAYER_ANTIFLICKER_HIGH:
            option->filter_mode = HI_TDE_FILTER_MODE_COLOR;
            break;
        case HIFB_LAYER_ANTIFLICKER_AUTO:
            option->filter_mode = HI_TDE_FILTER_MODE_COLOR;
            break;
        default:
            option->deflicker_mode = HI_TDE_DEFLICKER_LEVEL_MODE_NONE;
            break;
    }
    return;
}

static hi_void blit_opt_get_mirror_mode(hi_tde_opt *option, HIFB_BLIT_OPT_S *opt)
{
    switch (opt->enMirrorMode) {
        case HIFB_MIRROR_NONE:
            option->mirror = HI_TDE_MIRROR_NONE;
            break;
        case HIFB_MIRROR_HORIZONTAL:
            option->mirror = HI_TDE_MIRROR_HORIZONTAL;
            break;
        case HIFB_MIRROR_VERTICAL:
            option->mirror = HI_TDE_MIRROR_VERTICAL;
            break;
        case HIFB_MIRROR_BOTH:
            option->mirror = HI_TDE_MIRROR_BOTH;
            break;
        default:
            option->mirror = HI_TDE_MIRROR_NONE;
            break;
    }
    return;
}

static hi_void blit_opt_get_alpha(hi_tde_opt *option, HIFB_BLIT_OPT_S *opt)
{
    if (opt->stAlpha.bAlphaEnable) {
        option->alpha_blending_cmd = HI_TDE_ALPHA_BLENDING_BLEND;
        option->global_alpha = opt->stAlpha.u8GlobalAlpha;
        option->out_alpha_from = HI_TDE_OUT_ALPHA_FROM_NORM;
    } else {
        option->out_alpha_from = HI_TDE_OUT_ALPHA_FROM_FOREGROUND;
    }
    return;
}


static hi_void drv_blit_option_init(hi_tde_surface *src_surface, hi_tde_surface *dst_surface,
                                   hi_tde_opt *option, HIFB_BLIT_OPT_S *opt)
{
    option->resize = opt->bScale;
    if ((src_surface->color_fmt >= HIFB_DRV_ConvFmt(HIFB_FMT_1BPP)) &&
        (src_surface->color_fmt <= HIFB_DRV_ConvFmt(HIFB_FMT_ACLUT88))) {
        option->clut_reload = HI_TRUE;
        src_surface->clut_phy_addr = opt->u64CmapAddr;
        dst_surface->clut_phy_addr = opt->u64CmapAddr;
    }

    /* if opt is antiflicker level */
    blit_opt_get_filter_mode(option, opt);

    if (opt->stCKey.bKeyEnable) {
        if ((src_surface->color_fmt >= HIFB_DRV_ConvFmt(HIFB_FMT_1BPP)) &&
            (src_surface->color_fmt <= HIFB_DRV_ConvFmt(HIFB_FMT_ACLUT88))) {
            option->color_key_mode = HI_TDE_COLOR_KEY_MODE_FOREGROUND;
            option->color_key_value.clut_color_key.alpha.is_component_ignore = HI_TRUE;
            option->color_key_value.clut_color_key.clut.is_component_out = opt->stCKey.u32KeyMode;
            option->color_key_value.clut_color_key.clut.component_max = opt->stCKey.u8BlueMax;
            option->color_key_value.clut_color_key.clut.component_min = opt->stCKey.u8BlueMin;
        } else {
            option->color_key_mode = HI_TDE_COLOR_KEY_MODE_FOREGROUND;
            option->color_key_value.argb_color_key.alpha.is_component_ignore = HI_TRUE;
            option->color_key_value.argb_color_key.red.component_max = opt->stCKey.u8RedMax;
            option->color_key_value.argb_color_key.red.component_min = opt->stCKey.u8RedMin;
            option->color_key_value.argb_color_key.red.is_component_out = opt->stCKey.u32KeyMode;
            option->color_key_value.argb_color_key.red.component_mask = 0xff;
            option->color_key_value.argb_color_key.green.component_max = opt->stCKey.u8GreenMax;
            option->color_key_value.argb_color_key.green.component_min = opt->stCKey.u8GreenMin;
            option->color_key_value.argb_color_key.green.is_component_out = opt->stCKey.u32KeyMode;
            option->color_key_value.argb_color_key.green.component_mask = 0xff;
            option->color_key_value.argb_color_key.blue.component_max = opt->stCKey.u8BlueMax;
            option->color_key_value.argb_color_key.blue.component_min = opt->stCKey.u8BlueMin;
            option->color_key_value.argb_color_key.blue.is_component_out = opt->stCKey.u32KeyMode;
            option->color_key_value.argb_color_key.blue.component_mask = 0xff;
        }
    }

    /* if opt is alpha enable */
    blit_opt_get_alpha(option, opt);

    /* if opt is mirror mode */
    blit_opt_get_mirror_mode(option, opt);

    return;
}

static void drv_check_result(hi_s32 *handle, hi_s32 *ret, HIFB_TDECALLBACK_PARAM *param,
                            hi_tde_export_func *tde_export_func)
{
    if (*ret != HI_SUCCESS) {
        *ret = tde_export_func->drv_tde_module_cancel_job(*handle);
        if (*ret == HI_SUCCESS) {
            if (param) {
                kfree(param);
                param = NULL;
            }
        } else {
            HI_TRACE_FB(HI_DBG_ERR, "cancel job failed!ret = %x\n", *ret);
        }
    }
    return;
}

static hi_s32 drv_blit_start(hi_s32 *handle, hi_tde_surface *src_surface, hi_tde_rect *src_rect,
                             hi_tde_surface *dst_surface, hi_tde_rect *dst_rect, hi_tde_opt *option,
                             HIFB_BLIT_OPT_S *opt, hi_tde_export_func *tde_export_func)

{
    hi_s32 ret;
    HIFB_TDECALLBACK_PARAM *param = HI_NULL;
    hi_u32 time_out;
    ret = tde_export_func->drv_tde_module_begin_job(handle);
    if(ret != HI_SUCCESS) {
        return ret;
    }
    option->is_compress = opt->bCompress;
    ret = tde_export_func->drv_tde_module_blit(*handle, dst_surface, dst_rect, src_surface, src_rect,
                                               dst_surface, dst_rect, option);
    if(ret != HI_SUCCESS) {
        tde_export_func->drv_tde_module_cancel_job(*handle);
        return ret;
    }
    time_out = 100; /* timeout 100 */
#ifdef HI_FPGA
    /* ARGB1555 : widthxheight/8/period s, period is ASIC or FPGA CORE clock of tde. */
    /* eg. FPGA core 48MHz, 3840 * 2160 / 8 / (48 * 10 ^ 6) = 0.0216(s) */
    time_out = 432; /* 432 is 0.216 * 2 */
#endif
    if (opt->bCallBack) {
        if (in_atomic()) {
            param = kmalloc(sizeof(HIFB_TDECALLBACK_PARAM), GFP_ATOMIC);
        } else {
            param = kmalloc(sizeof(HIFB_TDECALLBACK_PARAM), GFP_KERNEL);
        }
        if (param == HI_NULL) {
            tde_export_func->drv_tde_module_cancel_job(*handle);
            return HI_FAILURE;
        }

        param->u32LayerId = *(HI_U32 *)opt->pParam;
        param->bSoftCursorUpdate = opt->bSoftCursorUpdate;
        param->bCompress = opt->bCompress;

        ret = tde_export_func->drv_tde_module_end_job(*handle, opt->bBlock, time_out, HI_FALSE,
                                                      (tde_func_cb)g_tde_call_back, param);
    } else {
        ret = tde_export_func->drv_tde_module_end_job(*handle, opt->bBlock, time_out, HI_FALSE,
                                                      HI_NULL, HI_NULL);
    }

    drv_check_result(handle,&ret,param,tde_export_func);
    return ret;
}


HI_S32 HIFB_DRV_Blit(HIFB_BUFFER_S *pSrcImg, HIFB_BUFFER_S *pDstImg, HIFB_BLIT_OPT_S *pstOpt,
                     HI_BOOL bRefreshScreen, TDE_HANDLE *refresh_handle)

{
    HI_S32 s32Ret,handle;
    hi_tde_surface stSrcSur = {0};
    hi_tde_surface stDstSur = {0};
    hi_tde_rect stSrcRect,stDstRect;
    hi_tde_opt option = {0};
    hi_tde_export_func *pTdeExportFunc = NULL;

    pTdeExportFunc = FUNC_ENTRY(hi_tde_export_func, HI_ID_TDE);
    if ((pTdeExportFunc == NULL) || (pTdeExportFunc->drv_tde_module_begin_job == NULL) ||
    (pTdeExportFunc->drv_tde_module_end_job == NULL) || (pTdeExportFunc->drv_tde_module_cancel_job == NULL) ||
    (pTdeExportFunc->drv_tde_module_enable_region_deflicker == NULL) ||
    (pTdeExportFunc->drv_tde_module_blit == NULL)) {
        HI_TRACE_FB(HI_DBG_ERR, "TDE pTdeExportFunc is NULL!\n");
        return HI_FAILURE;
    }

    /* src init */
    drv_blit_init(&stSrcSur, &stSrcRect, pSrcImg, pstOpt);
    /* dst_init */
    drv_blit_init(&stDstSur, &stDstRect, pDstImg, pstOpt);
    /* if compress */
    drv_blit_compress(&stDstSur, pDstImg, pstOpt);

    /* if is_refresh_screen */
    drv_blit_refresh_screen(&stSrcSur, &stSrcRect, &stDstSur, &stDstRect, bRefreshScreen);

    /* get option */
    drv_blit_option_init(&stSrcSur, &stDstSur, &option, pstOpt);

    /* if region deflicker open */
    if (pstOpt->bRegionDeflicker) {
        s32Ret = pTdeExportFunc->drv_tde_module_enable_region_deflicker(HI_TRUE);
        if (s32Ret != HI_SUCCESS) {
            HI_TRACE_FB(HI_DBG_ERR, "enable region deflicker failed!\n");
            return s32Ret;
        }
    }

    /* blit start */
    s32Ret = drv_blit_start(&handle, &stSrcSur, &stSrcRect, &stDstSur, &stDstRect, &option, pstOpt,
                            pTdeExportFunc);
    if (s32Ret != HI_SUCCESS) {
        HI_TRACE_FB(HI_DBG_ERR, "drv_blit_start failed!\n");
        return s32Ret;
    }
    if (refresh_handle != NULL) {
        *refresh_handle = handle;
    }
    /* if region deflicker close */
    if (pstOpt->bRegionDeflicker) {
        s32Ret = pTdeExportFunc->drv_tde_module_enable_region_deflicker(HI_FALSE);
        if (s32Ret != HI_SUCCESS) {
            HI_TRACE_FB(HI_DBG_ERR, "disable region deflicker failed!\n");
            return s32Ret;
        }
    }
    return HI_SUCCESS;
}


static hi_void drv_rotate_init(hi_tde_surface *surface, hi_tde_rect *rect, HIFB_BUFFER_S *src_img,
                               HIFB_BUFFER_S *dst_img)
{
    if (dst_img == NULL) {
        surface->phy_addr   = src_img->stCanvas.u64PhyAddr;
        surface->width  = src_img->stCanvas.u32Width;
        surface->height = src_img->stCanvas.u32Height;
        surface->stride = src_img->stCanvas.u32Pitch;
        surface->alpha_max_is_255 = HI_TRUE;
        surface->is_ycbcr_clut = HI_FALSE;
        surface->color_fmt = HIFB_DRV_ConvFmt(src_img->stCanvas.enFmt);

        rect->pos_x   = src_img->UpdateRect.x;
        rect->pos_y   = src_img->UpdateRect.y;
        rect->width  = src_img->UpdateRect.w;
        rect->height = src_img->UpdateRect.h;
    } else {
        surface->phy_addr      = dst_img->stCanvas.u64PhyAddr;
        surface->width     = dst_img->stCanvas.u32Width;
        surface->height    = dst_img->stCanvas.u32Height;
        surface->stride    = dst_img->stCanvas.u32Pitch;
        surface->alpha_max_is_255 = HI_TRUE;
        surface->is_ycbcr_clut   = HI_FALSE;
        surface->color_fmt   = HIFB_DRV_ConvFmt(dst_img->stCanvas.enFmt);

        rect->pos_x   = dst_img->UpdateRect.x;
        rect->pos_y   = dst_img->UpdateRect.y;
        rect->width  = src_img->UpdateRect.h;
        rect->height = src_img->UpdateRect.w;
    }

    return;
}

hi_void drv_get_rotate_mode(HIFB_ROTATE_OPT_S *rotate_opt, hi_tde_rotate_angle *tde_rotate_angle)
{
    switch(rotate_opt->enRotateMode)
    {
        case HIFB_ROTATE_90:
            *tde_rotate_angle = HI_TDE_ROTATE_CLOCKWISE_90;
            break;
        case HIFB_ROTATE_180:
            *tde_rotate_angle = HI_TDE_ROTATE_CLOCKWISE_180;
            break;
        case HIFB_ROTATE_270:
            *tde_rotate_angle = HI_TDE_ROTATE_CLOCKWISE_270;
            break;
        default:
            break;
    }
    return;
}



static HI_S32 drv_rotate_start(HI_S32 *handle, hi_tde_surface *src_surface, hi_tde_rect *src_rect,
                               hi_tde_surface *dst_surface, hi_tde_rect *dst_rect,
                               hi_tde_rotate_angle  tde_rotate_angle, HIFB_ROTATE_OPT_S *rotate_opt,
                               hi_tde_export_func *tde_export_func)
{
    HI_S32 ret;
    HIFB_TDECALLBACK_PARAM *param = HI_NULL;
    hi_u32 time_out;
    ret = tde_export_func->drv_tde_module_begin_job(handle);
    if(ret != HI_SUCCESS) {
        return ret;
    }

    ret = tde_export_func->drv_tde_module_rotate(*handle, src_surface, src_rect, dst_surface, dst_rect,
                                                 tde_rotate_angle);
    if(ret != HI_SUCCESS)
    {
        HI_TRACE_FB(HI_DBG_ERR, "tde rotate failed ret = 0x%x handle %d \n", ret, *handle);
        tde_export_func->drv_tde_module_cancel_job(*handle);
        return ret;
    }
    time_out = 100; /* timeout 100 */
#ifdef HI_FPGA
    /* ARGB1555 : widthxheight/8/period s, period is ASIC or FPGA CORE clock of tde. */
    /* eg. FPGA core 48MHz, 3840 * 2160 / 8 / (48 * 10 ^ 6) = 0.0216(s) */
    time_out = 432; /* 432 is 0.216 * 2 */
#endif

    if (rotate_opt->bCallBack) {
        if (in_atomic()) {
            param = kmalloc(sizeof(HIFB_TDECALLBACK_PARAM), GFP_ATOMIC);
        } else {
            param = kmalloc(sizeof(HIFB_TDECALLBACK_PARAM), GFP_KERNEL);
        }
        if (param == HI_NULL) {
            tde_export_func->drv_tde_module_cancel_job(*handle);
            return HI_FAILURE;
        }

        param->u32LayerId = *(hi_u32 *)rotate_opt->pParam;

        ret = tde_export_func->drv_tde_module_end_job(*handle, rotate_opt->bBlock, time_out, HI_FALSE,
                                                      (tde_func_cb)g_tde_rotate_call_back, param);
    } else {
        ret = tde_export_func->drv_tde_module_end_job(*handle, rotate_opt->bBlock, time_out, HI_FALSE,
                                                      HI_NULL, HI_NULL);
    }
    drv_check_result(handle,&ret,param,tde_export_func);
    return ret;
}

/* for graphic rotation. */
HI_S32 HIFB_DRV_Rotate(HIFB_BUFFER_S *src_img, HIFB_BUFFER_S *dst_img, HIFB_ROTATE_OPT_S *rotate_opt,
                       HI_BOOL is_refresh_screen)
{
    HI_S32 ret, handle;
    hi_tde_surface src_surface = {0};
    hi_tde_surface dst_surface = {0};
    hi_tde_rect src_rect = {0};
    hi_tde_rect dst_rect = {0};
    hi_tde_export_func *tde_export_func = NULL;
    hi_tde_rotate_angle  tde_rotate_angle = HI_TDE_ROTATE_MAX;

    tde_export_func = FUNC_ENTRY(hi_tde_export_func, HI_ID_TDE);
    if ((tde_export_func == NULL) ||
        (tde_export_func->drv_tde_module_begin_job == NULL) ||
        (tde_export_func->drv_tde_module_rotate == NULL) ||
        (tde_export_func->drv_tde_module_cancel_job == NULL) ||
        (tde_export_func->drv_tde_module_end_job == NULL)) {
        HI_TRACE_FB(HI_DBG_ERR, "TDE pTdeExportFunc is NULL!\n");
        return HI_FAILURE;
    }

    /* src init */
    drv_rotate_init(&src_surface, &src_rect, src_img, NULL);
    /* dst_init */
    drv_rotate_init(&dst_surface, &dst_rect, src_img, dst_img);

    /* get rotate mode */
    drv_get_rotate_mode(rotate_opt, &tde_rotate_angle);

    /* rotate start */
    ret = drv_rotate_start(&handle, &src_surface, &src_rect, &dst_surface, &dst_rect, tde_rotate_angle,
                           rotate_opt, tde_export_func);
    if (ret != HI_SUCCESS) {
        HI_TRACE_FB(HI_DBG_ERR, "drv_rotate_start failed!\n");
        return ret;
    }

    return handle;
}

HI_S32 HIFB_DRV_SetTdeCallBack(IntCallBack pTdeCallBack)
{
    g_tde_call_back = pTdeCallBack;
    return HI_SUCCESS;
}
HI_S32 HIFB_DRV_SetTdeRotateCallBack(IntCallBack pTdeRotCallBack)
{
    g_tde_rotate_call_back = pTdeRotCallBack;
    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */
